// 电影控制器
module controller.MovieController;

import std.conv : to;
import std.json : JSONValue, parseJSON;

import archttp.HttpStatusCode : HttpStatusCode;
import archttp.HttpRequest : HttpRequest;
import archttp.HttpResponse : HttpResponse;
import mir.ser.json : serializeJson;

import model.Movie;
import model.MovieVlink;
import repository.MovieRepo;

import utils.RespData : RespData;
import utils.DataCache;
import utils.CacheKey : CacheKey;
import utils.ObjMapping : ObjMapping;
import utils.SecurityVerification : signValidator;

/** 
 * 电影控制器类
 */
class MovieController
{
    class Obj
    {
        uint year;
        MovieDto[] list;

        this (uint year, MovieDto[] list)
        {
            this.year = year;
            this.list = list;
        }
    }

    MovieRepo _mrepo; // Movie仓储
    this ()
    {
        // 注入Movie仓储
        _mrepo = new MovieRepo();
    }

    /** 
     * 获取全部电影（年份分组）
     */
    public void getAllList (HttpRequest req, HttpResponse resp)
    {
        // 缓存中有数据直接取出返回
        auto data = cacheData.get(CacheKey.MOVIE_GLIST_CACHE);
        if (!data.isNull)
        {
            resp.json(RespData.newData(200, "ok", data.get)).send();
            return;
        }

        Movie[] movieList = _mrepo.getMovieList();
        alias DtoList = MovieDto[];
        DtoList[uint] mylist;
        foreach (m; movieList)
        {
            mylist[m.year] ~= ObjMapping.mappingToObject!(MovieDto, Movie)(m);
        }

        Obj[] objList;
        foreach (key, value; mylist)
        {
            objList ~= new Obj(key, value);
        }

        // 设置缓存
        string jsonList = serializeJson(objList);
        cacheData.put(CacheKey.MOVIE_GLIST_CACHE, jsonList);

        resp.json(RespData.newData(200, "ok", jsonList)).send();
    }

    /** 
     * 获取全部电影（不分组）
     */
    public void getListAll (HttpRequest req, HttpResponse resp)
    {
        // 缓存中有数据直接取出返回
        auto data = cacheData.get(CacheKey.MOVIE_ALIST_CACHE);
        if (!data.isNull)
        {
            resp.json(RespData.newData(200, "ok", data.get)).send();
            return;
        }

        Movie[] movieList = _mrepo.getMovieList();
        MovieDto[] dtoList = [];
        foreach (m; movieList)
        {
            dtoList ~= ObjMapping.mappingToObject!(MovieDto, Movie)(m);
        }

        // 设置缓存
        string jsonList = serializeJson(dtoList);
        cacheData.put(CacheKey.MOVIE_ALIST_CACHE, jsonList);

        resp.json(RespData.newData(200, "ok", jsonList)).send();
    }

    /** 
     * 分页获取列表数据
     */
    public void getList (HttpRequest req, HttpResponse resp)
    {
        import std.string : format;

        uint pageSize = to!uint(req.query("pageSize"));
        uint pageNum = to!uint(req.query("pageNum"));

        string dataCacheKey = format("%s%s%s", CacheKey.MOVIE_PLIST_CACHE, pageSize, pageNum);
        string totalPageCacheKey = format("%s%s%s", CacheKey.MOVIE_TOTALPAGE_CACHE, pageSize, pageNum);

        // 缓存中有数据直接取出返回
        auto data = cacheData.get(dataCacheKey);
        auto totalPageStr = cacheData.get(totalPageCacheKey);
        if ((!data.isNull) && (!totalPageStr.isNull))
        {
            int totalPage = to!int(totalPageStr.get);
            JSONValue respData = RespData.newData(200, "ok", data.get);
            respData.object["totalPage"] = JSONValue(totalPage);
            resp.json(respData).send();
            return;
        }

        Movie[] movieList = _mrepo.getMovieList(pageSize, pageNum);
        alias DtoList = MovieDto[];
        DtoList[uint] mylist;
        foreach (m; movieList)
        {
            mylist[m.year] ~= ObjMapping.mappingToObject!(MovieDto, Movie)(m);
        }

        Obj[] objList;
        foreach (key, value; mylist)
        {
            objList ~= new Obj(key, value);
        }

        uint total = _mrepo.getTotalCount();
        uint totalPage = (total + pageSize - 1) / pageSize;
        if (totalPage == 0)
        {
            totalPage = 1;
        }

        // 设置缓存
        string jsonList = serializeJson(objList);
        cacheData.put(dataCacheKey, jsonList);
        cacheData.put(totalPageCacheKey, to!string(totalPage));

        JSONValue respData = RespData.newData(200, "ok", jsonList);
        respData.object["totalPage"] = JSONValue(totalPage);
        resp.json(respData).send();
    }

    /** 
     * 获取一个电影
     */
    public void getOne (HttpRequest req, HttpResponse resp)
    {
        string id = req.param("id");
        if (id == "")
        {
            resp.json(RespData.newData(400, "错误的请求")).send();
        }
        Movie movie = _mrepo.getOneMovie(id);
        if (movie is null)
        {
            resp.code(HttpStatusCode.NOT_FOUND).json(RespData.newData(404, "未找到")).send();
            return;
        }

        JSONValue respData = RespData.newData!(MovieDto)(200, "ok", ObjMapping.mappingToObject!(MovieDto, Movie)(
                movie));
        resp.json(respData).send();
    }

    /** 
     * 获取集数列表
     */
    public void getVideoLinks (HttpRequest req, HttpResponse resp)
    {
        string id = req.query("mid");
        if (id is null || id == "")
        {
            resp.json(RespData.newData(400, "错误的请求")).send();
            return;
        }

        // 缓存中有数据直接取出返回
        auto data = cacheData.get(CacheKey.MOVIE_VLIST_CACHE ~ id);
        if (!data.isNull)
        {
            resp.json(RespData.newData(200, "ok", data.get)).send();
            return;
        }

        MovieVlink[] list = _mrepo.getMovieVideoLinks(id);
        MovieVlinkDto[] dtoList = [];
        foreach (ml; list)
        {
            dtoList ~= ObjMapping.mappingToObject!(MovieVlinkDto, MovieVlink)(ml);
        }

        // 设置缓存
        string jsonList = serializeJson(dtoList);
        cacheData.put(CacheKey.MOVIE_VLIST_CACHE ~ id, jsonList);

        resp.json(RespData.newData(200, "ok", jsonList)).send();
    }

    /** 
     * 获取所有年份
     */
    public void getYears (HttpRequest req, HttpResponse resp)
    {
        // 缓存中有数据直接取出返回
        auto data = cacheData.get(CacheKey.MOVIE_ALLYEAR_CACHE);
        if (!data.isNull)
        {
            resp.json(RespData.newData(200, "ok", data.get)).send();
            return;
        }

        uint[] years = _mrepo.getMovieYears();

        // 设置缓存
        string jsonList = serializeJson(years);
        cacheData.put(CacheKey.MOVIE_ALLYEAR_CACHE, jsonList);

        resp.json(RespData.newData(200, "ok", jsonList)).send();
    }

    /** 
     * 添加电影
     */
    public void addMovie (HttpRequest req, HttpResponse resp)
    {
        import std.digest : toHexString;
        import std.digest.md : md5Of;

        JSONValue json = parseJSON(req.body());
        string name = json["name"].str;
        string url = json["url"].str;
        string img = json["img"].str;
        string category = json["category"].str;
        string region = json["region"].str;
        uint year = to!uint(json["year"].str);
        string actors = json["actoes"].str;
        string director = json["director"].str;
        string intro = json["intro"].str;
        string comment = json["comment"].str;
        string id = to!string(toHexString(md5Of(name)));

        string sign = json["sign"].str;

        import std.format : format;

        string reqParamsStr = format("%s%s%s%s", name, url, category, year);
        if (!signValidator(reqParamsStr, sign))
        {
            resp.code(HttpStatusCode.FORBIDDEN).json(RespData.newData(403, "签名不正确！")).send();
            return;
        }

        MovieDtoFromCreate mdfc = new MovieDtoFromCreate(id, name, url, category, region, year, actors, director, intro, img, comment);

        bool res = _mrepo.addMovie(ObjMapping.mappingToObject!(Movie, MovieDtoFromCreate)(mdfc));
        if (!res)
        {
            resp.code(HttpStatusCode.INTERNAL_SERVER_ERROR)
                .json(RespData.newData(500, "error")).send();
            return;
        }
        resp.code(HttpStatusCode.CREATED).json(RespData.newData(200, "ok")).send();
    }

    /** 
     * 编辑电影
     */
    public void editMovie (HttpRequest req, HttpResponse resp)
    {
        string id = req.param("id");
        if (id == "")
        {
            resp.json(RespData.newData(400, "错误的请求")).send();
            return;
        }

        JSONValue json = parseJSON(req.body());
        string name = json["name"].str;
        string url = json["url"].str;
        string img = json["img"].str;
        string category = json["category"].str;
        string region = json["region"].str;
        uint year = to!uint(json["year"].str);
        string actors = json["actoes"].str;
        string director = json["director"].str;
        string intro = json["intro"].str;
        string comment = json["comment"].str;

        string sign = json["sign"].str;

        import std.format : format;

        string reqParamsStr = format("%s%s%s%s", name, url, category, year);
        if (!signValidator(reqParamsStr, sign))
        {
            resp.code(HttpStatusCode.FORBIDDEN).json(RespData.newData(403, "签名不正确！")).send();
            return;
        }

        MovieDtoFromUpdate mdfu = new MovieDtoFromUpdate(name, url, category, region, year, actors, director, intro, img, comment);
        bool res = _mrepo.updateMovie(id, ObjMapping.mappingToObject!(Movie, MovieDtoFromUpdate)(
                mdfu));
        if (!res)
        {
            resp.code(HttpStatusCode.INTERNAL_SERVER_ERROR)
                .json(RespData.newData(500, "error")).send();
            return;
        }
        resp.json(RespData.newData(200, "ok")).send();
    }

    /** 
     * 删除电影
     */
    public void deleteMovie (HttpRequest req, HttpResponse resp)
    {
        string id = req.param("id");
        string sign = req.query("sign");
        if (id == "" || sign == "" || sign is null)
        {
            resp.json(RespData.newData(400, "错误的请求")).send();
            return;
        }

        if (!signValidator(id, sign))
        {
            resp.code(HttpStatusCode.FORBIDDEN).json(RespData.newData(403, "签名不正确！")).send();
            return;
        }

        bool res = _mrepo.removeMovie(id);
        if (!res)
        {
            resp.code(HttpStatusCode.INTERNAL_SERVER_ERROR)
                .json(RespData.newData(500, "error")).send();
            return;
        }
        resp.json(RespData.newData(200, "ok")).send();
    }

    /** 
     * 写评语
     */
    public void doComment (HttpRequest req, HttpResponse resp)
    {
        string id = req.param("id");
        if (id == "")
        {
            resp.json(RespData.newData(400, "错误的请求")).send();
            return;
        }

        JSONValue json = parseJSON(req.body());
        string comment = json["comment"].str;
        if (comment.length < 1)
        {
            auto respData = RespData.newData(400, "请输入评语");
            resp.code(HttpStatusCode.BAD_REQUEST).json(respData).send();
            return;
        }
        bool res = _mrepo.doComment(id, comment);
        if (!res)
        {
            resp.code(HttpStatusCode.INTERNAL_SERVER_ERROR)
                .json(RespData.newData(500, "error")).send();
            return;
        }
        resp.json(RespData.newData(200, "ok")).send();
    }
}
